package nacos

import (
	"Nacos_GateWay/config"
	"Nacos_GateWay/logcat"
	"Nacos_GateWay/utils"
	"github.com/nacos-group/nacos-sdk-go/clients"
	"github.com/nacos-group/nacos-sdk-go/clients/naming_client"
	"github.com/nacos-group/nacos-sdk-go/common/constant"
	"github.com/nacos-group/nacos-sdk-go/model"
	"github.com/nacos-group/nacos-sdk-go/vo"
	"io"
	"io/ioutil"
	"log"
	"net"
	"os"
	"strconv"
)

var iClient naming_client.INamingClient

func Run(conf *config.Config) {
	//加载nacos配置
	//conf := config.LoadConfig("config-nacos.yml")
	//创建clientConfig
	clientConfig := constant.ClientConfig{
		NamespaceId:         conf.GetString("nacos.clientConfig.NamespaceId"), // 如果需要支持多namespace，我们可以场景多个client,它们有不同的NamespaceId
		TimeoutMs:           conf.GetUInt64("nacos.clientConfig.TimeoutMs"),
		NotLoadCacheAtStart: true,
		LogDir:              conf.GetString("nacos.clientConfig.LogDir"),
		CacheDir:            conf.GetString("nacos.clientConfig.CacheDir"),
		RotateTime:          "1h",
		MaxAge:              3,
		LogLevel:            conf.GetString("nacos.clientConfig.LogLevel"),
	}
	//创建服务配置
	serverConfigs := []constant.ServerConfig{
		{
			IpAddr:      conf.GetString("nacos.serverConfig.IpAddr"),
			ContextPath: "/nacos",
			Port:        conf.GetUInt64("nacos.serverConfig.Port"),
			Scheme:      "http",
		},
	}

	// 创建服务发现客户端的另一种方式 (推荐)
	namingClient, namingClienterr := clients.NewNamingClient(
		vo.NacosClientParam{
			ClientConfig:  &clientConfig,
			ServerConfigs: serverConfigs,
		},
	)
	if namingClienterr != nil {
		logcat.Instance().Info(namingClienterr)
		return
	}
	iClient = namingClient
	// 创建动态配置客户端的另一种方式 (推荐)
	configClient, configClienterr := clients.NewConfigClient(
		vo.NacosClientParam{
			ClientConfig:  &clientConfig,
			ServerConfigs: serverConfigs,
		},
	)
	if configClienterr != nil {
		logcat.Instance().Info(configClienterr)
	}
	logcat.Instance().Info("##获取nacos配置")
	//先获取nacos上配置
	nacosCfgContent, getcfgerr := configClient.GetConfig(vo.ConfigParam{
		DataId: conf.GetString("nacos.registerConfig.ServiceName"),
		Group:  conf.GetString("nacos.registerConfig.GroupName")})
	//如果nacos上的配置为空
	if getcfgerr != nil || nacosCfgContent == "" {
		logcat.Instance().Info("###获取nacos网络配置异常:", getcfgerr)
		//读取本地文件配置
		file, readfileerr := ioutil.ReadFile(conf.FilePath)
		if readfileerr != nil {
			logcat.Instance().Info("读取本地配置异常：", readfileerr)
		}
		cfgcontent := string(file)
		//发布配置
		success, err := configClient.PublishConfig(vo.ConfigParam{
			DataId:  conf.GetString("nacos.registerConfig.DataId"),
			Group:   conf.GetString("nacos.registerConfig.GroupName"),
			Content: cfgcontent})
		logcat.Instance().Info("###发布配置结果:", success, "err:", err, "dataid:", conf.GetString("nacos.registerConfig.ServiceName"))
	} else {
		WriteConfig(nacosCfgContent)
	}

	port := conf.GetUInt64("web.port")
	weight, _ := strconv.ParseFloat(conf.GetString("nacos.registerConfig.Weight"), 64)
	logcat.Instance().Info(" 服务名:", conf.GetString("nacos.registerConfig.ServiceName"), " 端口:", port, " 权重:", weight, " ip: ", getLocalIp())
	//注册实例
	namingsuccess, namingerr := namingClient.RegisterInstance(vo.RegisterInstanceParam{
		Ip:          getLocalIp(),
		Port:        port,
		ServiceName: conf.GetString("nacos.registerConfig.ServiceName"),
		Weight:      weight,
		Enable:      true,
		Healthy:     true,
		Ephemeral:   true,
		Metadata:    map[string]string{"preserved.register.source": "SPRING_CLOUD"},
		ClusterName: conf.GetString("nacos.registerConfig.ClusterName"), // 默认值DEFAULT
		GroupName:   conf.GetString("nacos.registerConfig.GroupName"),   // 默认值DEFAULT_GROUP
	})
	logcat.Instance().Info("###namingerr:", namingerr)
	if namingsuccess {
		logcat.Instance().Info("nacos注册成功")
	} else {
		logcat.Instance().Info("nacos注册失败")
	}
	//监听配置变化
	listencfgErr := configClient.ListenConfig(vo.ConfigParam{
		DataId: conf.GetString("nacos.registerConfig.DataId"),
		Group:  conf.GetString("nacos.registerConfig.GroupName"),
		OnChange: func(namespace, group, dataId, data string) {
			logcat.Instance().Info("group:" + group + ", dataId:" + dataId + ", data:" + data)
			//重新拉取配置文件写入本地
			WriteConfig(data)
			//重新加载配置文件
			cfg := config.LoadConfig("config.yml")
			//解析配置到内存
			Unmarshal(cfg)
		},
	})
	if listencfgErr != nil {
		logcat.Instance().Info("listencfgErr:", listencfgErr)
	}
	cfg := config.LoadConfig("config.yml")
	Unmarshal(cfg)
}

//写入本地配置文件
func WriteConfig(nacosCfgContent string) {
	//保存配置 写入文件 判断本地文件是否存在
	isExit, err := utils.FilePathExists("config.yml")
	if isExit {
		//文件存在 打开文件写入配置
		f, openerr := os.OpenFile("config.yml", os.O_RDWR|os.O_TRUNC|os.O_CREATE, 0766)
		if openerr != nil {
			logcat.Instance().Info("打开本地配置文件异常:", openerr)
		} else {
			_, writeErr := io.WriteString(f, nacosCfgContent)
			if writeErr != nil {
				logcat.Instance().Info("nacos写入本地配置异常:", writeErr)
			}
		}
	} else {
		//文件不存在
		if err != nil {
			logcat.Instance().Info(err)
		}
		//创建文件
		utils.CreateAndWriteFile("config.yml", nacosCfgContent)
	}
}

/**
 * 解析网关配置
 */
func Unmarshal(conf *config.Config) {

	err := conf.Viper.Unmarshal(&config.ConfigAll)
	if err != nil {
		logcat.Instance().Info("gateway error:", err)
	}
	routes := config.ConfigAll.Gateway.Routes
	gateway := config.Routes()
	for _, v := range routes {
		instance, getServiceErr := GetOneHealthyService(v.Id)
		if getServiceErr != nil {
			logcat.Instance().Error("getServiceErr:", getServiceErr)
		}
		//组装数据存到内存
		if instance != nil {
			r := config.NewRoute()
			r.Port = instance.Port
			r.Ip = instance.Ip
			r.Id = v.Id
			r.Url = v.Url
			gateway.Gates = append(gateway.Gates, r)
		}
	}
}

//获取一个健康的实例
func GetOneHealthyService(serviceName string) (*model.Instance, error) {
	instance, err := iClient.SelectOneHealthyInstance(vo.SelectOneHealthInstanceParam{
		ServiceName: serviceName,
	})

	return instance, err
}

//获取本地IP
func getLocalIp() string {
	netInterfaces, err := net.Interfaces()
	if err != nil {
		logcat.Instance().Info("net.Interfaces failed, err:", err.Error())
		return ""
	}
	for i := 0; i < len(netInterfaces); i++ {
		if (netInterfaces[i].Flags & net.FlagUp) != 0 {
			addrs, _ := netInterfaces[i].Addrs()

			for _, address := range addrs {
				if ipnet, ok := address.(*net.IPNet); ok && !ipnet.IP.IsLoopback() {
					if ipnet.IP.To4() != nil {
						log.Println(ipnet.IP.String())
						return ipnet.IP.String()
					}
				}
			}
		}
	}
	return ""
}
